define(function (require, exports, module) {
    return function (jQuery) {

        /**
         * jquery.Jcrop.js v0.9.10
         * jQuery Image Cropping Plugin - released under MIT License 
         * Author: Kelly Hallman <khallman@gmail.com>
         * http://github.com/tapmodo/Jcrop
         * Copyright (c) 2008-2012 Tapmodo Interactive LLC {{{
         *
         * Permission is hereby granted, free of charge, to any person
         * obtaining a copy of this software and associated documentation
         * files (the "Software"), to deal in the Software without
         * restriction, including without limitation the rights to use,
         * copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the
         * Software is furnished to do so, subject to the following
         * conditions:
         *
         * The above copyright notice and this permission notice shall be
         * included in all copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
         * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
         * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
         * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
         * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
         * OTHER DEALINGS IN THE SOFTWARE.
         *
         * }}}
         */

        (function ($) {

            $.Jcrop = function (obj, opt) {
                var options = $.extend({}, $.Jcrop.defaults),
                    docOffset, lastcurs, ie6mode = false;

                // Internal Methods {{{
                function px(n) {
                    return n + 'px';
                }
                function cssClass(cl) {
                    return options.baseClass + '-' + cl;
                }
                function supportsColorFade() {
                    return $.fx.step.hasOwnProperty('backgroundColor');
                }
                function getPos(obj) //{{{
                {
                    var pos = $(obj).offset();
                    return [pos.left, pos.top];
                }
                //}}}
                function mouseAbs(e) //{{{
                {
                    return [(e.pageX - docOffset[0]), (e.pageY - docOffset[1])];
                }
                //}}}
                function setOptions(opt) //{{{
                {
                    if (typeof (opt) !== 'object') opt = {};
                    options = $.extend(options, opt);

                    $.each(['onChange', 'onSelect', 'onRelease', 'onDblClick'], function (i, e) {
                        if (typeof (options[e]) !== 'function') options[e] = function () { };
                    });
                }
                //}}}
                function startDragMode(mode, pos) //{{{
                {
                    docOffset = getPos($img);
                    Tracker.setCursor(mode === 'move' ? mode : mode + '-resize');

                    if (mode === 'move') {
                        return Tracker.activateHandlers(createMover(pos), doneSelect);
                    }

                    var fc = Coords.getFixed();
                    var opp = oppLockCorner(mode);
                    var opc = Coords.getCorner(oppLockCorner(opp));

                    Coords.setPressed(Coords.getCorner(opp));
                    Coords.setCurrent(opc);

                    Tracker.activateHandlers(dragmodeHandler(mode, fc), doneSelect);
                }
                //}}}
                function dragmodeHandler(mode, f) //{{{
                {
                    return function (pos) {
                        if (!options.aspectRatio) {
                            switch (mode) {
                                case 'e':
                                    pos[1] = f.y2;
                                    break;
                                case 'w':
                                    pos[1] = f.y2;
                                    break;
                                case 'n':
                                    pos[0] = f.x2;
                                    break;
                                case 's':
                                    pos[0] = f.x2;
                                    break;
                            }
                        } else {
                            switch (mode) {
                                case 'e':
                                    pos[1] = f.y + 1;
                                    break;
                                case 'w':
                                    pos[1] = f.y + 1;
                                    break;
                                case 'n':
                                    pos[0] = f.x + 1;
                                    break;
                                case 's':
                                    pos[0] = f.x + 1;
                                    break;
                            }
                        }
                        Coords.setCurrent(pos);
                        Selection.update();
                    };
                }
                //}}}
                function createMover(pos) //{{{
                {
                    var lloc = pos;
                    KeyManager.watchKeys();

                    return function (pos) {
                        Coords.moveOffset([pos[0] - lloc[0], pos[1] - lloc[1]]);
                        lloc = pos;

                        Selection.update();
                    };
                }
                //}}}
                function oppLockCorner(ord) //{{{
                {
                    switch (ord) {
                        case 'n':
                            return 'sw';
                        case 's':
                            return 'nw';
                        case 'e':
                            return 'nw';
                        case 'w':
                            return 'ne';
                        case 'ne':
                            return 'sw';
                        case 'nw':
                            return 'se';
                        case 'se':
                            return 'nw';
                        case 'sw':
                            return 'ne';
                    }
                }
                //}}}
                function createDragger(ord) //{{{
                {
                    return function (e) {
                        if (options.disabled) {
                            return false;
                        }
                        if ((ord === 'move') && !options.allowMove) {
                            return false;
                        }

                        // Fix position of crop area when dragged the very first time.
                        // Necessary when crop image is in a hidden element when page is loaded.
                        docOffset = getPos($img);

                        btndown = true;
                        startDragMode(ord, mouseAbs(e));
                        e.stopPropagation();
                        e.preventDefault();
                        return false;
                    };
                }
                //}}}
                function presize($obj, w, h) //{{{
                {
                    var nw = $obj.width(),
                        nh = $obj.height();
                    if ((nw > w) && w > 0) {
                        nw = w;
                        nh = (w / $obj.width()) * $obj.height();
                    }
                    if ((nh > h) && h > 0) {
                        nh = h;
                        nw = (h / $obj.height()) * $obj.width();
                    }
                    xscale = $obj.width() / nw;
                    yscale = $obj.height() / nh;
                    $obj.width(nw).height(nh);
                }
                //}}}
                function unscale(c) //{{{
                {
                    return {
                        x: c.x * xscale,
                        y: c.y * yscale,
                        x2: c.x2 * xscale,
                        y2: c.y2 * yscale,
                        w: c.w * xscale,
                        h: c.h * yscale
                    };
                }
                //}}}
                function doneSelect(pos) //{{{
                {
                    var c = Coords.getFixed();
                    if ((c.w > options.minSelect[0]) && (c.h > options.minSelect[1])) {
                        Selection.enableHandles();
                        Selection.done();
                    } else {
                        Selection.release();
                    }
                    Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default');
                }
                //}}}
                function newSelection(e) //{{{
                {
                    if (options.disabled) {
                        return false;
                    }
                    if (!options.allowSelect) {
                        return false;
                    }
                    btndown = true;
                    docOffset = getPos($img);
                    Selection.disableHandles();
                    Tracker.setCursor('crosshair');
                    var pos = mouseAbs(e);
                    Coords.setPressed(pos);
                    Selection.update();
                    Tracker.activateHandlers(selectDrag, doneSelect);
                    KeyManager.watchKeys();

                    e.stopPropagation();
                    e.preventDefault();
                    return false;
                }
                //}}}
                function selectDrag(pos) //{{{
                {
                    Coords.setCurrent(pos);
                    Selection.update();
                }
                //}}}
                function newTracker() //{{{
                {
                    var trk = $('<div></div>').addClass(cssClass('tracker'));
                    if ($.browser.msie) {
                        trk.css({
                            opacity: 0,
                            backgroundColor: 'white'
                        });
                    }
                    return trk;
                }
                //}}}

                // }}}
                // Initialization {{{
                // Sanitize some options {{{
                if ($.browser.msie && ($.browser.version.split('.')[0] === '6')) {
                    ie6mode = true;
                }
                if (typeof (obj) !== 'object') {
                    obj = $(obj)[0];
                }
                if (typeof (opt) !== 'object') {
                    opt = {};
                }
                // }}}
                setOptions(opt);
                // Initialize some jQuery objects {{{
                // The values are SET on the image(s) for the interface
                // If the original image has any of these set, they will be reset
                // However, if you destroy() the Jcrop instance the original image's
                // character in the DOM will be as you left it.
                var img_css = {
                    border: 'none',
                    visibility: 'visible',
                    margin: 0,
                    padding: 0,
                    position: 'absolute',
                    top: 0,
                    left: 0
                };

                var $origimg = $(obj),
                  img_mode = true;

                if (obj.tagName == 'IMG') {
                    // Fix size of crop image.
                    // Necessary when crop image is within a hidden element when page is loaded.
                    if ($origimg[0].width != 0 && $origimg[0].height != 0) {
                        // Obtain dimensions from contained img element.
                        $origimg.width($origimg[0].width);
                        $origimg.height($origimg[0].height);
                    } else {
                        // Obtain dimensions from temporary image in case the original is not loaded yet (e.g. IE 7.0). 
                        var tempImage = new Image();
                        tempImage.src = $origimg[0].src;
                        $origimg.width(tempImage.width);
                        $origimg.height(tempImage.height);
                    }

                    var $img = $origimg.clone().removeAttr('id').css(img_css).show();

                    $img.width($origimg.width());
                    $img.height($origimg.height());
                    $origimg.after($img).hide();

                } else {
                    $img = $origimg.css(img_css).show();
                    img_mode = false;
                    if (options.shade === null) { options.shade = true; }
                }

                presize($img, options.boxWidth, options.boxHeight);

                var boundx = $img.width(),
                    boundy = $img.height(),


                    $div = $('<div />').width(boundx).height(boundy).addClass(cssClass('holder')).css({
                        position: 'relative',
                        backgroundColor: options.bgColor
                    }).insertAfter($origimg).append($img);

                if (options.addClass) {
                    $div.addClass(options.addClass);
                }

                var $img2 = $('<div />'),

                    $img_holder = $('<div />')
                    .width('100%').height('100%').css({
                        zIndex: 310,
                        position: 'absolute',
                        overflow: 'hidden'
                    }),

                    $hdl_holder = $('<div />')
                    .width('100%').height('100%').css('zIndex', 320),

                    $sel = $('<div />')
                    .css({
                        position: 'absolute',
                        zIndex: 600
                    }).dblclick(function () {
                        var c = Coords.getFixed();
                        options.onDblClick.call(api, c);
                    }).insertBefore($img).append($img_holder, $hdl_holder);

                if (img_mode) {

                    $img2 = $('<img />')
                        .attr('src', $img.attr('src')).css(img_css).width(boundx).height(boundy),

                    $img_holder.append($img2);

                }

                if (ie6mode) {
                    $sel.css({
                        overflowY: 'hidden'
                    });
                }

                var bound = options.boundary;
                var $trk = newTracker().width(boundx + (bound * 2)).height(boundy + (bound * 2)).css({
                    position: 'absolute',
                    top: px(-bound),
                    left: px(-bound),
                    zIndex: 290
                }).mousedown(newSelection);

                /* }}} */
                // Set more variables {{{
                var bgcolor = options.bgColor,
                    bgopacity = options.bgOpacity,
                    xlimit, ylimit, xmin, ymin, xscale, yscale, enabled = true,
                    btndown, animating, shift_down;

                docOffset = getPos($img);
                // }}}
                // }}}
                // Internal Modules {{{
                // Touch Module {{{ 
                var Touch = (function () {
                    // Touch support detection function adapted (under MIT License)
                    // from code by Jeffrey Sambells - http://github.com/iamamused/
                    function hasTouchSupport() {
                        var support = {},
                            events = ['touchstart', 'touchmove', 'touchend'],
                            el = document.createElement('div'), i;

                        try {
                            for (i = 0; i < events.length; i++) {
                                var eventName = events[i];
                                eventName = 'on' + eventName;
                                var isSupported = (eventName in el);
                                if (!isSupported) {
                                    el.setAttribute(eventName, 'return;');
                                    isSupported = typeof el[eventName] == 'function';
                                }
                                support[events[i]] = isSupported;
                            }
                            return support.touchstart && support.touchend && support.touchmove;
                        }
                        catch (err) {
                            return false;
                        }
                    }

                    function detectSupport() {
                        if ((options.touchSupport === true) || (options.touchSupport === false)) return options.touchSupport;
                        else return hasTouchSupport();
                    }
                    return {
                        createDragger: function (ord) {
                            return function (e) {
                                e.pageX = e.originalEvent.changedTouches[0].pageX;
                                e.pageY = e.originalEvent.changedTouches[0].pageY;
                                if (options.disabled) {
                                    return false;
                                }
                                if ((ord === 'move') && !options.allowMove) {
                                    return false;
                                }
                                btndown = true;
                                startDragMode(ord, mouseAbs(e));
                                e.stopPropagation();
                                e.preventDefault();
                                return false;
                            };
                        },
                        newSelection: function (e) {
                            e.pageX = e.originalEvent.changedTouches[0].pageX;
                            e.pageY = e.originalEvent.changedTouches[0].pageY;
                            return newSelection(e);
                        },
                        isSupported: hasTouchSupport,
                        support: detectSupport()
                    };
                }());
                // }}}
                // Coords Module {{{
                var Coords = (function () {
                    var x1 = 0,
                        y1 = 0,
                        x2 = 0,
                        y2 = 0,
                        ox, oy;

                    function setPressed(pos) //{{{
                    {
                        pos = rebound(pos);
                        x2 = x1 = pos[0];
                        y2 = y1 = pos[1];
                    }
                    //}}}
                    function setCurrent(pos) //{{{
                    {
                        pos = rebound(pos);
                        ox = pos[0] - x2;
                        oy = pos[1] - y2;
                        x2 = pos[0];
                        y2 = pos[1];
                    }
                    //}}}
                    function getOffset() //{{{
                    {
                        return [ox, oy];
                    }
                    //}}}
                    function moveOffset(offset) //{{{
                    {
                        var ox = offset[0],
                            oy = offset[1];

                        if (0 > x1 + ox) {
                            ox -= ox + x1;
                        }
                        if (0 > y1 + oy) {
                            oy -= oy + y1;
                        }

                        if (boundy < y2 + oy) {
                            oy += boundy - (y2 + oy);
                        }
                        if (boundx < x2 + ox) {
                            ox += boundx - (x2 + ox);
                        }

                        x1 += ox;
                        x2 += ox;
                        y1 += oy;
                        y2 += oy;
                    }
                    //}}}
                    function getCorner(ord) //{{{
                    {
                        var c = getFixed();
                        switch (ord) {
                            case 'ne':
                                return [c.x2, c.y];
                            case 'nw':
                                return [c.x, c.y];
                            case 'se':
                                return [c.x2, c.y2];
                            case 'sw':
                                return [c.x, c.y2];
                        }
                    }
                    //}}}
                    function getFixed() //{{{
                    {
                        if (!options.aspectRatio) {
                            return getRect();
                        }
                        // This function could use some optimization I think...
                        var aspect = options.aspectRatio,
                            min_x = options.minSize[0] / xscale,


                            //min_y = options.minSize[1]/yscale,
                            max_x = options.maxSize[0] / xscale,
                            max_y = options.maxSize[1] / yscale,
                            rw = x2 - x1,
                            rh = y2 - y1,
                            rwa = Math.abs(rw),
                            rha = Math.abs(rh),
                            real_ratio = rwa / rha,
                            xx, yy, w, h;

                        if (max_x === 0) {
                            max_x = boundx * 10;
                        }
                        if (max_y === 0) {
                            max_y = boundy * 10;
                        }
                        if (real_ratio < aspect) {
                            yy = y2;
                            w = rha * aspect;
                            xx = rw < 0 ? x1 - w : w + x1;

                            if (xx < 0) {
                                xx = 0;
                                h = Math.abs((xx - x1) / aspect);
                                yy = rh < 0 ? y1 - h : h + y1;
                            } else if (xx > boundx) {
                                xx = boundx;
                                h = Math.abs((xx - x1) / aspect);
                                yy = rh < 0 ? y1 - h : h + y1;
                            }
                        } else {
                            xx = x2;
                            h = rwa / aspect;
                            yy = rh < 0 ? y1 - h : y1 + h;
                            if (yy < 0) {
                                yy = 0;
                                w = Math.abs((yy - y1) * aspect);
                                xx = rw < 0 ? x1 - w : w + x1;
                            } else if (yy > boundy) {
                                yy = boundy;
                                w = Math.abs(yy - y1) * aspect;
                                xx = rw < 0 ? x1 - w : w + x1;
                            }
                        }

                        // Magic %-)
                        if (xx > x1) { // right side
                            if (xx - x1 < min_x) {
                                xx = x1 + min_x;
                            } else if (xx - x1 > max_x) {
                                xx = x1 + max_x;
                            }
                            if (yy > y1) {
                                yy = y1 + (xx - x1) / aspect;
                            } else {
                                yy = y1 - (xx - x1) / aspect;
                            }
                        } else if (xx < x1) { // left side
                            if (x1 - xx < min_x) {
                                xx = x1 - min_x;
                            } else if (x1 - xx > max_x) {
                                xx = x1 - max_x;
                            }
                            if (yy > y1) {
                                yy = y1 + (x1 - xx) / aspect;
                            } else {
                                yy = y1 - (x1 - xx) / aspect;
                            }
                        }

                        if (xx < 0) {
                            x1 -= xx;
                            xx = 0;
                        } else if (xx > boundx) {
                            x1 -= xx - boundx;
                            xx = boundx;
                        }

                        if (yy < 0) {
                            y1 -= yy;
                            yy = 0;
                        } else if (yy > boundy) {
                            y1 -= yy - boundy;
                            yy = boundy;
                        }

                        return makeObj(flipCoords(x1, y1, xx, yy));
                    }
                    //}}}
                    function rebound(p) //{{{
                    {
                        if (p[0] < 0) {
                            p[0] = 0;
                        }
                        if (p[1] < 0) {
                            p[1] = 0;
                        }

                        if (p[0] > boundx) {
                            p[0] = boundx;
                        }
                        if (p[1] > boundy) {
                            p[1] = boundy;
                        }

                        return [p[0], p[1]];
                    }
                    //}}}
                    function flipCoords(x1, y1, x2, y2) //{{{
                    {
                        var xa = x1,
                            xb = x2,
                            ya = y1,
                            yb = y2;
                        if (x2 < x1) {
                            xa = x2;
                            xb = x1;
                        }
                        if (y2 < y1) {
                            ya = y2;
                            yb = y1;
                        }
                        return [xa, ya, xb, yb];
                    }
                    //}}}
                    function getRect() //{{{
                    {
                        var xsize = x2 - x1,
                            ysize = y2 - y1,
                            delta;

                        if (xlimit && (Math.abs(xsize) > xlimit)) {
                            x2 = (xsize > 0) ? (x1 + xlimit) : (x1 - xlimit);
                        }
                        if (ylimit && (Math.abs(ysize) > ylimit)) {
                            y2 = (ysize > 0) ? (y1 + ylimit) : (y1 - ylimit);
                        }

                        if (ymin / yscale && (Math.abs(ysize) < ymin / yscale)) {
                            y2 = (ysize > 0) ? (y1 + ymin / yscale) : (y1 - ymin / yscale);
                        }
                        if (xmin / xscale && (Math.abs(xsize) < xmin / xscale)) {
                            x2 = (xsize > 0) ? (x1 + xmin / xscale) : (x1 - xmin / xscale);
                        }

                        if (x1 < 0) {
                            x2 -= x1;
                            x1 -= x1;
                        }
                        if (y1 < 0) {
                            y2 -= y1;
                            y1 -= y1;
                        }
                        if (x2 < 0) {
                            x1 -= x2;
                            x2 -= x2;
                        }
                        if (y2 < 0) {
                            y1 -= y2;
                            y2 -= y2;
                        }
                        if (x2 > boundx) {
                            delta = x2 - boundx;
                            x1 -= delta;
                            x2 -= delta;
                        }
                        if (y2 > boundy) {
                            delta = y2 - boundy;
                            y1 -= delta;
                            y2 -= delta;
                        }
                        if (x1 > boundx) {
                            delta = x1 - boundy;
                            y2 -= delta;
                            y1 -= delta;
                        }
                        if (y1 > boundy) {
                            delta = y1 - boundy;
                            y2 -= delta;
                            y1 -= delta;
                        }

                        return makeObj(flipCoords(x1, y1, x2, y2));
                    }
                    //}}}
                    function makeObj(a) //{{{
                    {
                        return {
                            x: a[0],
                            y: a[1],
                            x2: a[2],
                            y2: a[3],
                            w: a[2] - a[0],
                            h: a[3] - a[1]
                        };
                    }
                    //}}}

                    return {
                        flipCoords: flipCoords,
                        setPressed: setPressed,
                        setCurrent: setCurrent,
                        getOffset: getOffset,
                        moveOffset: moveOffset,
                        getCorner: getCorner,
                        getFixed: getFixed
                    };
                }());

                //}}}
                // Shade Module {{{
                var Shade = (function () {
                    var enabled = false,
                        holder = $('<div />').css({
                            position: 'absolute',
                            zIndex: 240,
                            opacity: 0
                        }),
                        shades = {
                            top: createShade(),
                            left: createShade().height(boundy),
                            right: createShade().height(boundy),
                            bottom: createShade()
                        };

                    function resizeShades(w, h) {
                        shades.left.css({ height: px(h) });
                        shades.right.css({ height: px(h) });
                    }
                    function updateAuto() {
                        return updateShade(Coords.getFixed());
                    }
                    function updateShade(c) {
                        shades.top.css({
                            left: px(c.x),
                            width: px(c.w),
                            height: px(c.y)
                        });
                        shades.bottom.css({
                            top: px(c.y2),
                            left: px(c.x),
                            width: px(c.w),
                            height: px(boundy - c.y2)
                        });
                        shades.right.css({
                            left: px(c.x2),
                            width: px(boundx - c.x2)
                        });
                        shades.left.css({
                            width: px(c.x)
                        });
                    }
                    function createShade() {
                        return $('<div />').css({
                            position: 'absolute',
                            backgroundColor: options.shadeColor || options.bgColor
                        }).appendTo(holder);
                    }
                    function enableShade() {
                        if (!enabled) {
                            enabled = true;
                            holder.insertBefore($img);
                            updateAuto();
                            Selection.setBgOpacity(1, 0, 1);
                            $img2.hide();

                            setBgColor(options.shadeColor || options.bgColor, 1);
                            if (Selection.isAwake()) {
                                setOpacity(options.bgOpacity, 1);
                            }
                            else setOpacity(1, 1);
                        }
                    }
                    function setBgColor(color, now) {
                        colorChangeMacro(getShades(), color, now);
                    }
                    function disableShade() {
                        if (enabled) {
                            holder.remove();
                            $img2.show();
                            enabled = false;
                            if (Selection.isAwake()) {
                                Selection.setBgOpacity(options.bgOpacity, 1, 1);
                            } else {
                                Selection.setBgOpacity(1, 1, 1);
                                Selection.disableHandles();
                            }
                            colorChangeMacro($div, 0, 1);
                        }
                    }
                    function setOpacity(opacity, now) {
                        if (enabled) {
                            if (options.bgFade && !now) {
                                holder.animate({
                                    opacity: 1 - opacity
                                }, {
                                    queue: false,
                                    duration: options.fadeTime
                                });
                            }
                            else holder.css({ opacity: 1 - opacity });
                        }
                    }
                    function refreshAll() {
                        options.shade ? enableShade() : disableShade();
                        if (Selection.isAwake()) setOpacity(options.bgOpacity);
                    }
                    function getShades() {
                        return holder.children();
                    }

                    return {
                        update: updateAuto,
                        updateRaw: updateShade,
                        getShades: getShades,
                        setBgColor: setBgColor,
                        enable: enableShade,
                        disable: disableShade,
                        resize: resizeShades,
                        refresh: refreshAll,
                        opacity: setOpacity
                    };
                }());
                // }}}
                // Selection Module {{{
                var Selection = (function () {
                    var awake,
                        hdep = 370,
                        borders = {},
                        handle = {},
                        dragbar = {},
                        seehandles = false;

                    // Private Methods
                    function insertBorder(type) //{{{
                    {
                        var jq = $('<div />').css({
                            position: 'absolute',
                            opacity: options.borderOpacity
                        }).addClass(cssClass(type));
                        $img_holder.append(jq);
                        return jq;
                    }
                    //}}}
                    function dragDiv(ord, zi) //{{{
                    {
                        var jq = $('<div />').mousedown(createDragger(ord)).css({
                            cursor: ord + '-resize',
                            position: 'absolute',
                            zIndex: zi
                        }).addClass('ord-' + ord);

                        if (Touch.support) {
                            jq.bind('touchstart.jcrop', Touch.createDragger(ord));
                        }

                        $hdl_holder.append(jq);
                        return jq;
                    }
                    //}}}
                    function insertHandle(ord) //{{{
                    {
                        var hs = options.handleSize;
                        return dragDiv(ord, hdep++).css({
                            opacity: options.handleOpacity
                        }).width(hs).height(hs).addClass(cssClass('handle'));
                    }
                    //}}}
                    function insertDragbar(ord) //{{{
                    {
                        return dragDiv(ord, hdep++).addClass('jcrop-dragbar');
                    }
                    //}}}
                    function createDragbars(li) //{{{
                    {
                        var i;
                        for (i = 0; i < li.length; i++) {
                            dragbar[li[i]] = insertDragbar(li[i]);
                        }
                    }
                    //}}}
                    function createBorders(li) //{{{
                    {
                        var cl, i;
                        for (i = 0; i < li.length; i++) {
                            switch (li[i]) {
                                case 'n': cl = 'hline'; break;
                                case 's': cl = 'hline bottom'; break;
                                case 'e': cl = 'vline right'; break;
                                case 'w': cl = 'vline'; break;
                            }
                            borders[li[i]] = insertBorder(cl);
                        }
                    }
                    //}}}
                    function createHandles(li) //{{{
                    {
                        var i;
                        for (i = 0; i < li.length; i++) {
                            handle[li[i]] = insertHandle(li[i]);
                        }
                    }
                    //}}}
                    function moveto(x, y) //{{{
                    {
                        if (!options.shade) {
                            $img2.css({
                                top: px(-y),
                                left: px(-x)
                            });
                        }
                        $sel.css({
                            top: px(y),
                            left: px(x)
                        });
                    }
                    //}}}
                    function resize(w, h) //{{{
                    {
                        $sel.width(w).height(h);
                    }
                    //}}}
                    function refresh() //{{{
                    {
                        var c = Coords.getFixed();

                        Coords.setPressed([c.x, c.y]);
                        Coords.setCurrent([c.x2, c.y2]);

                        updateVisible();
                    }
                    //}}}

                    // Internal Methods
                    function updateVisible(select) //{{{
                    {
                        if (awake) {
                            return update(select);
                        }
                    }
                    //}}}
                    function update(select) //{{{
                    {
                        var c = Coords.getFixed();

                        resize(c.w, c.h);
                        moveto(c.x, c.y);
                        if (options.shade) Shade.updateRaw(c);

                        awake || show();

                        if (select) {
                            options.onSelect.call(api, unscale(c));
                        } else {
                            options.onChange.call(api, unscale(c));
                        }
                    }
                    //}}}
                    function setBgOpacity(opacity, force, now) //{{{
                    {
                        if (!awake && !force) return;
                        if (options.bgFade && !now) {
                            $img.animate({
                                opacity: opacity
                            }, {
                                queue: false,
                                duration: options.fadeTime
                            });
                        } else {
                            $img.css('opacity', opacity);
                        }
                    }
                    //}}}
                    function show() //{{{
                    {
                        $sel.show();

                        if (options.shade) Shade.opacity(bgopacity);
                        else setBgOpacity(bgopacity, true);

                        awake = true;
                    }
                    //}}}
                    function release() //{{{
                    {
                        disableHandles();
                        $sel.hide();

                        if (options.shade) Shade.opacity(1);
                        else setBgOpacity(1);

                        awake = false;
                        options.onRelease.call(api);
                    }
                    //}}}
                    function showHandles() //{{{
                    {
                        if (seehandles) {
                            $hdl_holder.show();
                        }
                    }
                    //}}}
                    function enableHandles() //{{{
                    {
                        seehandles = true;
                        if (options.allowResize) {
                            $hdl_holder.show();
                            return true;
                        }
                    }
                    //}}}
                    function disableHandles() //{{{
                    {
                        seehandles = false;
                        $hdl_holder.hide();
                    }
                    //}}}
                    function animMode(v) //{{{
                    {
                        if (animating === v) {
                            disableHandles();
                        } else {
                            enableHandles();
                        }
                    }
                    //}}}
                    function done() //{{{
                    {
                        animMode(false);
                        refresh();
                    }
                    //}}}
                    // Insert draggable elements {{{
                    // Insert border divs for outline

                    if (options.dragEdges && $.isArray(options.createDragbars))
                        createDragbars(options.createDragbars);

                    if ($.isArray(options.createHandles))
                        createHandles(options.createHandles);

                    if (options.drawBorders && $.isArray(options.createBorders))
                        createBorders(options.createBorders);

                    //}}}

                    // This is a hack for iOS5 to support drag/move touch functionality
                    $(document).bind('touchstart.jcrop-ios', function (e) {
                        if ($(e.currentTarget).hasClass('jcrop-tracker')) e.stopPropagation();
                    });

                    var $track = newTracker().mousedown(createDragger('move')).css({
                        cursor: 'move',
                        position: 'absolute',
                        zIndex: 360
                    });

                    if (Touch.support) {
                        $track.bind('touchstart.jcrop', Touch.createDragger('move'));
                    }

                    $img_holder.append($track);
                    disableHandles();

                    return {
                        updateVisible: updateVisible,
                        update: update,
                        release: release,
                        refresh: refresh,
                        isAwake: function () {
                            return awake;
                        },
                        setCursor: function (cursor) {
                            $track.css('cursor', cursor);
                        },
                        enableHandles: enableHandles,
                        enableOnly: function () {
                            seehandles = true;
                        },
                        showHandles: showHandles,
                        disableHandles: disableHandles,
                        animMode: animMode,
                        setBgOpacity: setBgOpacity,
                        done: done
                    };
                }());

                //}}}
                // Tracker Module {{{
                var Tracker = (function () {
                    var onMove = function () { },
                        onDone = function () { },
                        trackDoc = options.trackDocument;

                    function toFront() //{{{
                    {
                        $trk.css({
                            zIndex: 450
                        });
                        if (Touch.support) {
                            $(document)
                              .bind('touchmove.jcrop', trackTouchMove)
                              .bind('touchend.jcrop', trackTouchEnd);
                        }
                        if (trackDoc) {
                            $(document)
                              .bind('mousemove.jcrop', trackMove)
                              .bind('mouseup.jcrop', trackUp);
                        }
                    }
                    //}}}
                    function toBack() //{{{
                    {
                        $trk.css({
                            zIndex: 290
                        });
                        $(document).unbind('.jcrop');
                    }
                    //}}}
                    function trackMove(e) //{{{
                    {
                        onMove(mouseAbs(e));
                        return false;
                    }
                    //}}}
                    function trackUp(e) //{{{
                    {
                        e.preventDefault();
                        e.stopPropagation();

                        if (btndown) {
                            btndown = false;

                            onDone(mouseAbs(e));

                            if (Selection.isAwake()) {
                                options.onSelect.call(api, unscale(Coords.getFixed()));
                            }

                            toBack();
                            onMove = function () { };
                            onDone = function () { };
                        }

                        return false;
                    }
                    //}}}
                    function activateHandlers(move, done) //{{{
                    {
                        btndown = true;
                        onMove = move;
                        onDone = done;
                        toFront();
                        return false;
                    }
                    //}}}
                    function trackTouchMove(e) //{{{
                    {
                        e.pageX = e.originalEvent.changedTouches[0].pageX;
                        e.pageY = e.originalEvent.changedTouches[0].pageY;
                        return trackMove(e);
                    }
                    //}}}
                    function trackTouchEnd(e) //{{{
                    {
                        e.pageX = e.originalEvent.changedTouches[0].pageX;
                        e.pageY = e.originalEvent.changedTouches[0].pageY;
                        return trackUp(e);
                    }
                    //}}}
                    function setCursor(t) //{{{
                    {
                        $trk.css('cursor', t);
                    }
                    //}}}

                    if (!trackDoc) {
                        $trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp);
                    }

                    $img.before($trk);
                    return {
                        activateHandlers: activateHandlers,
                        setCursor: setCursor
                    };
                }());
                //}}}
                // KeyManager Module {{{
                var KeyManager = (function () {
                    var $keymgr = $('<input type="radio" />').css({
                        position: 'fixed',
                        left: '-120px',
                        width: '12px'
                    }),
                        $keywrap = $('<div />').css({
                            position: 'absolute',
                            overflow: 'hidden'
                        }).append($keymgr);

                    function watchKeys() //{{{
                    {
                        if (options.keySupport) {
                            $keymgr.show();
                            $keymgr.focus();
                        }
                    }
                    //}}}
                    function onBlur(e) //{{{
                    {
                        $keymgr.hide();
                    }
                    //}}}
                    function doNudge(e, x, y) //{{{
                    {
                        if (options.allowMove) {
                            Coords.moveOffset([x, y]);
                            Selection.updateVisible(true);
                        }
                        e.preventDefault();
                        e.stopPropagation();
                    }
                    //}}}
                    function parseKey(e) //{{{
                    {
                        if (e.ctrlKey || e.metaKey) {
                            return true;
                        }
                        shift_down = e.shiftKey ? true : false;
                        var nudge = shift_down ? 10 : 1;

                        switch (e.keyCode) {
                            case 37:
                                doNudge(e, -nudge, 0);
                                break;
                            case 39:
                                doNudge(e, nudge, 0);
                                break;
                            case 38:
                                doNudge(e, 0, -nudge);
                                break;
                            case 40:
                                doNudge(e, 0, nudge);
                                break;
                            case 27:
                                if (options.allowSelect) Selection.release();
                                break;
                            case 9:
                                return true;
                        }

                        return false;
                    }
                    //}}}

                    if (options.keySupport) {
                        $keymgr.keydown(parseKey).blur(onBlur);
                        if (ie6mode || !options.fixedSupport) {
                            $keymgr.css({
                                position: 'absolute',
                                left: '-20px'
                            });
                            $keywrap.append($keymgr).insertBefore($img);
                        } else {
                            $keymgr.insertBefore($img);
                        }
                    }


                    return {
                        watchKeys: watchKeys
                    };
                }());
                //}}}
                // }}}
                // API methods {{{
                function setClass(cname) //{{{
                {
                    $div.removeClass().addClass(cssClass('holder')).addClass(cname);
                }
                //}}}
                function animateTo(a, callback) //{{{
                {
                    var x1 = a[0] / xscale,
                        y1 = a[1] / yscale,
                        x2 = a[2] / xscale,
                        y2 = a[3] / yscale;

                    if (animating) {
                        return;
                    }

                    var animto = Coords.flipCoords(x1, y1, x2, y2),
                        c = Coords.getFixed(),
                        initcr = [c.x, c.y, c.x2, c.y2],
                        animat = initcr,
                        interv = options.animationDelay,
                        ix1 = animto[0] - initcr[0],
                        iy1 = animto[1] - initcr[1],
                        ix2 = animto[2] - initcr[2],
                        iy2 = animto[3] - initcr[3],
                        pcent = 0,
                        velocity = options.swingSpeed;

                    x = animat[0];
                    y = animat[1];
                    x2 = animat[2];
                    y2 = animat[3];

                    Selection.animMode(true);
                    var anim_timer;

                    function queueAnimator() {
                        window.setTimeout(animator, interv);
                    }
                    var animator = (function () {
                        return function () {
                            pcent += (100 - pcent) / velocity;

                            animat[0] = x + ((pcent / 100) * ix1);
                            animat[1] = y + ((pcent / 100) * iy1);
                            animat[2] = x2 + ((pcent / 100) * ix2);
                            animat[3] = y2 + ((pcent / 100) * iy2);

                            if (pcent >= 99.8) {
                                pcent = 100;
                            }
                            if (pcent < 100) {
                                setSelectRaw(animat);
                                queueAnimator();
                            } else {
                                Selection.done();
                                if (typeof (callback) === 'function') {
                                    callback.call(api);
                                }
                            }
                        };
                    }());
                    queueAnimator();
                }
                //}}}
                function setSelect(rect) //{{{
                {
                    setSelectRaw([rect[0] / xscale, rect[1] / yscale, rect[2] / xscale, rect[3] / yscale]);
                    options.onSelect.call(api, unscale(Coords.getFixed()));
                    Selection.enableHandles();
                }
                //}}}
                function setSelectRaw(l) //{{{
                {
                    Coords.setPressed([l[0], l[1]]);
                    Coords.setCurrent([l[2], l[3]]);
                    Selection.update();
                }
                //}}}
                function tellSelect() //{{{
                {
                    return unscale(Coords.getFixed());
                }
                //}}}
                function tellScaled() //{{{
                {
                    return Coords.getFixed();
                }
                //}}}
                function setOptionsNew(opt) //{{{
                {
                    setOptions(opt);
                    interfaceUpdate();
                }
                //}}}
                function disableCrop() //{{{
                {
                    options.disabled = true;
                    Selection.disableHandles();
                    Selection.setCursor('default');
                    Tracker.setCursor('default');
                }
                //}}}
                function enableCrop() //{{{
                {
                    options.disabled = false;
                    interfaceUpdate();
                }
                //}}}
                function cancelCrop() //{{{
                {
                    Selection.done();
                    Tracker.activateHandlers(null, null);
                }
                //}}}
                function destroy() //{{{
                {
                    $div.remove();
                    $origimg.show();
                    $(obj).removeData('Jcrop');
                }
                //}}}
                function setImage(src, callback) //{{{
                {
                    Selection.release();
                    disableCrop();
                    var img = new Image();
                    img.onload = function () {
                        var iw = img.width;
                        var ih = img.height;
                        var bw = options.boxWidth;
                        var bh = options.boxHeight;
                        $img.width(iw).height(ih);
                        $img.attr('src', src);
                        $img2.attr('src', src);
                        presize($img, bw, bh);
                        boundx = $img.width();
                        boundy = $img.height();
                        $img2.width(boundx).height(boundy);
                        $trk.width(boundx + (bound * 2)).height(boundy + (bound * 2));
                        $div.width(boundx).height(boundy);
                        Shade.resize(boundx, boundy);
                        enableCrop();

                        if (typeof (callback) === 'function') {
                            callback.call(api);
                        }
                    };
                    img.src = src;
                }
                //}}}
                function colorChangeMacro($obj, color, now) {
                    var mycolor = color || options.bgColor;
                    if (options.bgFade && supportsColorFade() && options.fadeTime && !now) {
                        $obj.animate({
                            backgroundColor: mycolor
                        }, {
                            queue: false,
                            duration: options.fadeTime
                        });
                    } else {
                        $obj.css('backgroundColor', mycolor);
                    }
                }
                function interfaceUpdate(alt) //{{{
                    // This method tweaks the interface based on options object.
                    // Called when options are changed and at end of initialization.
                {
                    if (options.allowResize) {
                        if (alt) {
                            Selection.enableOnly();
                        } else {
                            Selection.enableHandles();
                        }
                    } else {
                        Selection.disableHandles();
                    }

                    Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default');
                    Selection.setCursor(options.allowMove ? 'move' : 'default');

                    if (options.hasOwnProperty('trueSize')) {
                        xscale = options.trueSize[0] / boundx;
                        yscale = options.trueSize[1] / boundy;
                    }

                    if (options.hasOwnProperty('setSelect')) {
                        setSelect(options.setSelect);
                        Selection.done();
                        delete (options.setSelect);
                    }

                    Shade.refresh();

                    if (options.bgColor != bgcolor) {
                        colorChangeMacro(
                          options.shade ? Shade.getShades() : $div,
                          options.shade ?
                            (options.shadeColor || options.bgColor) :
                            options.bgColor
                        );
                        bgcolor = options.bgColor;
                    }

                    if (bgopacity != options.bgOpacity) {
                        bgopacity = options.bgOpacity;
                        if (options.shade) Shade.refresh();
                        else Selection.setBgOpacity(bgopacity);
                    }

                    xlimit = options.maxSize[0] || 0;
                    ylimit = options.maxSize[1] || 0;
                    xmin = options.minSize[0] || 0;
                    ymin = options.minSize[1] || 0;

                    if (options.hasOwnProperty('outerImage')) {
                        $img.attr('src', options.outerImage);
                        delete (options.outerImage);
                    }

                    Selection.refresh();
                }
                //}}}
                //}}}

                if (Touch.support) $trk.bind('touchstart.jcrop', Touch.newSelection);

                $hdl_holder.hide();
                interfaceUpdate(true);

                var api = {
                    setImage: setImage,
                    animateTo: animateTo,
                    setSelect: setSelect,
                    setOptions: setOptionsNew,
                    tellSelect: tellSelect,
                    tellScaled: tellScaled,
                    setClass: setClass,

                    disable: disableCrop,
                    enable: enableCrop,
                    cancel: cancelCrop,
                    release: Selection.release,
                    destroy: destroy,

                    focus: KeyManager.watchKeys,

                    getBounds: function () {
                        return [boundx * xscale, boundy * yscale];
                    },
                    getWidgetSize: function () {
                        return [boundx, boundy];
                    },
                    getScaleFactor: function () {
                        return [xscale, yscale];
                    },
                    getOptions: function () {
                        // careful: internal values are returned
                        return options;
                    },

                    ui: {
                        holder: $div,
                        selection: $sel
                    }
                };

                if ($.browser.msie)
                    $div.bind('selectstart', function () { return false; });

                $origimg.data('Jcrop', api);
                return api;
            };
            $.fn.Jcrop = function (options, callback) //{{{
            {
                var api;
                // Iterate over each object, attach Jcrop
                this.each(function () {
                    // If we've already attached to this object
                    if ($(this).data('Jcrop')) {
                        // The API can be requested this way (undocumented)
                        if (options === 'api') return $(this).data('Jcrop');
                            // Otherwise, we just reset the options...
                        else $(this).data('Jcrop').setOptions(options);
                    }
                        // If we haven't been attached, preload and attach
                    else {
                        if (this.tagName == 'IMG')
                            $.Jcrop.Loader(this, function () {
                                $(this).css({ display: 'block', visibility: 'hidden' });
                                api = $.Jcrop(this, options);
                                if ($.isFunction(callback)) callback.call(api);
                            });
                        else {
                            $(this).css({ display: 'block', visibility: 'hidden' });
                            api = $.Jcrop(this, options);
                            if ($.isFunction(callback)) callback.call(api);
                        }
                    }
                });

                // Return "this" so the object is chainable (jQuery-style)
                return this;
            };
            //}}}
            // $.Jcrop.Loader - basic image loader {{{

            $.Jcrop.Loader = function (imgobj, success, error) {
                var $img = $(imgobj), img = $img[0];

                function completeCheck() {
                    if (img.complete) {
                        $img.unbind('.jcloader');
                        if ($.isFunction(success)) success.call(img);
                    }
                    else window.setTimeout(completeCheck, 50);
                }

                $img
                  .bind('load.jcloader', completeCheck)
                  .bind('error.jcloader', function (e) {
                      $img.unbind('.jcloader');
                      if ($.isFunction(error)) error.call(img);
                  });

                if (img.complete && $.isFunction(success)) {
                    $img.unbind('.jcloader');
                    success.call(img);
                }
            };

            //}}}
            // Global Defaults {{{
            $.Jcrop.defaults = {

                // Basic Settings
                allowSelect: true,
                allowMove: true,
                allowResize: true,

                trackDocument: true,

                // Styling Options
                baseClass: 'jcrop',
                addClass: null,
                bgColor: 'black',
                bgOpacity: 0.6,
                bgFade: false,
                borderOpacity: 0.4,
                handleOpacity: 0.5,
                handleSize: 7,

                aspectRatio: 0,
                keySupport: true,
                createHandles: ['n', 's', 'e', 'w', 'nw', 'ne', 'se', 'sw'],
                createDragbars: ['n', 's', 'e', 'w'],
                createBorders: ['n', 's', 'e', 'w'],
                drawBorders: true,
                dragEdges: true,
                fixedSupport: true,
                touchSupport: null,

                shade: null,

                boxWidth: 0,
                boxHeight: 0,
                boundary: 2,
                fadeTime: 400,
                animationDelay: 20,
                swingSpeed: 3,

                minSelect: [0, 0],
                maxSize: [0, 0],
                minSize: [0, 0],

                // Callbacks / Event Handlers
                onChange: function () { },
                onSelect: function () { },
                onDblClick: function () { },
                onRelease: function () { }
            };

            // }}}
        }(jQuery));

    };
});